package com.sinosoft.gateway.zuul;

import com.google.common.collect.Lists;
import com.netflix.zuul.context.RequestContext;
import com.sinosoft.gateway.config.Constants;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * Created by xujingfeng on 2017/4/1.
 */
public class CustomRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator{

    public final static Logger log = LoggerFactory.getLogger(CustomRouteLocator.class);

//    private JdbcTemplate jdbcTemplate;

    private ZuulProperties properties;

    private PathMatcher pathMatcher = new AntPathMatcher();

//    public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
//        this.jdbcTemplate = jdbcTemplate;
//    }

    public CustomRouteLocator(String servletPath, ZuulProperties properties) {
        super(servletPath, properties);
        this.properties = properties;
        log.info("servletPath:{}",servletPath);
    }

    //父类已经提供了这个方法，这里写出来只是为了说明这一个方法很重要！！！
//    @Override
//    protected void doRefresh() {
//        super.doRefresh();
//    }


    @Override
    public void refresh() {
        doRefresh();
    }

    @Override
    protected Map<String, ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
        //从application.properties中加载路由信息
        routesMap.putAll(super.locateRoutes());
        //从db中加载路由信息
        routesMap.putAll(locateRoutesFromDB());
        //优化一下配置
//        LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
//        for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
//            String path = entry.getKey();
//            // Prepend with slash if not already present.
//            if (!path.startsWith("/")) {
//                path = "/" + path;
//            }
//            if (StringUtils.hasText(this.properties.getPrefix())) {
//                path = this.properties.getPrefix() + path;
//                if (!path.startsWith("/")) {
//                    path = "/" + path;
//                }
//            }
//            values.put(path, entry.getValue());
//        }
        return routesMap;
    }

    private Map<String, ZuulRoute> locateRoutesFromDB(){
        Map<String, ZuulRoute> routes = new LinkedHashMap<>();
//        List<ZuulRouteVO> results = jdbcTemplate.query("select * from gateway_api_define where enabled = true ",new BeanPropertyRowMapper<>(ZuulRouteVO.class));
//        INSERT INTO gateway_api_define (id, path, service_id, retryable, strip_prefix, url, enabled) VALUES ('xxxx', '/xxxx/**', null,0,1, 'http://localhost:8090', 1);
        List<ZuulRouteVO> results = Lists.newArrayList(
                ZuulRouteVO.builder().id("device_id_1").path("/book/**").retryable(false).stripPrefix(true).url("http://localhost:8090").enabled(true).build(),
                ZuulRouteVO.builder().id("devoce_id_2").path("/uac/**").retryable(false).stripPrefix(true).url("http://localhost:8090").enabled(true).build(),
                ZuulRouteVO.builder().id("device_id_3").path("/common.audit.srv/**").retryable(false).stripPrefix(true).url("http://localhost:8090/book").enabled(true).build());
        for (ZuulRouteVO result : results) {
            if(org.apache.commons.lang3.StringUtils.isBlank(result.getPath()) || org.apache.commons.lang3.StringUtils.isBlank(result.getUrl()) ){
                continue;
            }
            ZuulRoute zuulRoute = new ZuulRoute();
            try {
                org.springframework.beans.BeanUtils.copyProperties(result,zuulRoute);
            } catch (Exception e) {
                log.error("=============load zuul route info from db with error==============",e);
            }
            routes.put(zuulRoute.getId(),zuulRoute); //device_id 为主键
        }
        return routes;
    }


    protected ZuulRoute getZuulRoute(String adjustedPath) {
        if(RequestContext.getCurrentContext().get(Constants.IS_DEBUG_REQUEST_FLAG) != null){
            return null;
        }
        if (!this.matchesIgnoredPatterns(adjustedPath)) {
            Map<String, ZuulRoute> zouteMap = this.getRoutesMap();
            RequestContext ctx = RequestContext.getCurrentContext();
            String device_id = (String)ctx.get("device_id");
            ZuulRoute zuulRoute = zouteMap.get(device_id);
            if(zuulRoute == null){
                log.warn("current device is not regist!");
                return null;
            }
            if (this.pathMatcher.match(zuulRoute.getPath(), adjustedPath)) {
                return zuulRoute;
            }
        }
        return null;
    }



    @Builder
    @Getter
    @Setter
    public static class ZuulRouteVO {

        /**
         * The ID of the route (the same as its map key by default).
         */
        private String id;

        /**
         * The path (pattern) for the route, e.g. /foo/**.
         */
        private String path;

        /**
         * The service ID (if any) to map to this route. You can specify a physical URL or
         * a service, but not both.
         */
        private String serviceId;

        /**
         * A full physical URL to map to the route. An alternative is to use a service ID
         * and service discovery to find the physical address.
         */
        private String url;

        /**
         * Flag to determine whether the prefix for this route (the path, minus pattern
         * patcher) should be stripped before forwarding.
         */
        private boolean stripPrefix = true;

        /**
         * Flag to indicate that this route should be retryable (if supported). Generally
         * retry requires a service ID and ribbon.
         */
        private Boolean retryable;

        private String apiName;

        private Boolean enabled;

    }
}
